package cn.ywang.pattern.proxy.custom;

import javax.tools.JavaCompiler;
import javax.tools.StandardJavaFileManager;
import javax.tools.ToolProvider;
import java.io.File;
import java.io.FileWriter;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;

/**
 * @author BeatificWang
 * @date 2019/3/11 23:34
 */
public class Proxy {

	private static final String nextLine = "\r\n";

	public static Object newProxyInstance(ClassLoader classLoader, Class<?>[] interfaces,
	                                      InvocationHandler h) {

		try {
			// 1. 动态生成源代码--java文件
			String src = generateJavaSrc(interfaces);

			// 2. java文件输出至磁盘
			String filePath = Proxy.class.getResource("").getPath();
			System.out.println(filePath);
			File file = new File(filePath + "$Proxy0.java");
			FileWriter fileWriter = new FileWriter(file);
			fileWriter.write(src);
			fileWriter.flush();
			fileWriter.close();
			// 3. java文件变成class文件
			JavaCompiler javaCompiler = ToolProvider.getSystemJavaCompiler();
			StandardJavaFileManager manager = javaCompiler.getStandardFileManager(null, null, null);
			Iterable iterable = manager.getJavaFileObjects(file);
			JavaCompiler.CompilationTask task = javaCompiler.getTask(null, manager, null, null, null, iterable);
			task.call();
			manager.close();
			// 4. 编译生成的.class文件加载到JVM中
			Class<?> clazz = classLoader.findClass("$Proxy0");
			Constructor constructor = clazz.getConstructor(InvocationHandler.class);
			// 5. 返回字节码重组后的新的代理对象
			return constructor.newInstance(h);
		} catch (Exception e) {
			e.printStackTrace();
		}

		return null;
	}


	private static String generateJavaSrc(Class<?>[] interfaces) {
		StringBuffer sb = new StringBuffer();
		sb.append("package cn.ywang.pattern.proxy.custom;" + nextLine);
		sb.append("import cn.ywang.pattern.proxy.target.Person;" + nextLine);
		sb.append("import cn.ywang.pattern.proxy.custom.InvocationHandler;" + nextLine);
		sb.append("import java.lang.reflect.Method;" + nextLine);
		sb.append("import " + interfaces[0].getCanonicalName() + ";" + nextLine);
		sb.append("public class $Proxy0 implements " + interfaces[0].getName() + "{" + nextLine);
		sb.append("private InvocationHandler h;" + nextLine);
		sb.append("public $Proxy0(InvocationHandler h) {" + nextLine);
		sb.append("this.h=h;" + nextLine);
		sb.append("}" + nextLine);
		for (Method method : interfaces[0].getMethods()) {
			sb.append("public " + method.getReturnType().getName() + " " + method.getName() + "(){" + nextLine);
			// 方法体
			sb.append("try{" + nextLine);
			sb.append("Method m=" + interfaces[0].getName() + ".class.getMethod(\"" + method.getName()
					          + "\",new Class[]{});" + nextLine);
			sb.append("this.h.invoke(this,m,null);" + nextLine);
			sb.append("} catch(Throwable e){" + nextLine);
			sb.append("e.printStackTrace();" + nextLine);
			sb.append("}" + nextLine);
			sb.append("}" + nextLine);
		}
		sb.append("}" + "\r\n");

		return sb.toString();
	}
}
